import redis
import json
import os
from dotenv import load_dotenv
from typing import Optional, Any, Dict, List
import time

load_dotenv()



class RedisClient:
    def __init__(self):
        self.host = os.getenv('REDIS_HOST', 'localhost')
        self.port = int(os.getenv('REDIS_PORT', 6379))
        self.password = os.getenv('REDIS_PASSWORD', None)
        self.db = int(os.getenv('REDIS_DB', 0))
        self.expire = int(os.getenv('REDIS_EXPIRY', 86400))  # 修复变量名
        self.redis_client = None
        self._connect()
        self.queue1_key = "bind_queue:success:"
        self.queue2_key = "bind_queue:failed:"
        self.processing_key_prefix = "processing:"

    def _connect(self):
        """连接Redis"""
        try:
            self.redis_client = redis.Redis(
                host=self.host,
                port=self.port,
                password=self.password,
                db=self.db,
                decode_responses=True,
                socket_connect_timeout=5,
                retry_on_timeout=True
            )
            # 测试连接
            self.redis_client.ping()
            print("Redis连接成功")
        except Exception as e:
            print(f"Redis连接失败: {e}")
            self.redis_client = None

    def is_connected(self) -> bool:
        """检查Redis连接状态"""
        if not self.redis_client:
            return False
        try:
            return self.redis_client.ping()
        except:
            return False

    def set_token(self, token: str, user_data: Dict[str, Any]) -> bool:
        """存储token和用户数据"""
        if not self.is_connected():
            return False

        try:
            key = f"token:{token}"
            value = json.dumps(user_data)
            self.redis_client.setex(key, self.expire, value)
            return True
        except Exception as e:
            print(f"存储token失败: {e}")
            return False

    def get_token_data(self, token: str) -> Optional[Dict[str, Any]]:
        """根据token获取用户数据"""
        if not self.is_connected():
            return None

        try:
            key = f"token:{token}"
            value = self.redis_client.get(key)
            if value:
                # 续期
                self.redis_client.expire(key, self.expire)
                return json.loads(value)
            return None
        except Exception as e:
            print(f"获取token数据失败: {e}")
            return None

    def delete_token(self, token: str) -> bool:
        """删除token"""
        if not self.is_connected():
            return False

        try:
            key = f"token:{token}"
            result = self.redis_client.delete(key)
            return result > 0
        except Exception as e:
            print(f"删除token失败: {e}")
            return False

    def set_user_tokens(self, user_id: int, tokens: List[str]) -> bool:
        """存储用户的所有token"""
        if not self.is_connected():
            return False

        try:
            key = f"user_tokens:{user_id}"
            self.redis_client.setex(key, self.expire, json.dumps(tokens))
            return True
        except Exception as e:
            print(f"存储用户token列表失败: {e}")
            return False

    def get_user_tokens(self, user_id: int) -> List[str]:
        """获取用户的所有token"""
        if not self.is_connected():
            return []

        try:
            key = f"user_tokens:{user_id}"
            value = self.redis_client.get(key)
            if value:
                # 续期
                self.redis_client.expire(key, self.expire)
                return json.loads(value)
            return []
        except Exception as e:
            print(f"获取用户token列表失败: {e}")
            return []

    def add_user_token(self, user_id: int, token: str) -> bool:
        """添加用户token到列表"""
        tokens = self.get_user_tokens(user_id)
        tokens.append(token)
        return self.set_user_tokens(user_id, tokens)

    def remove_user_token(self, user_id: int, token: str) -> bool:
        """从用户token列表中移除指定token"""
        tokens = self.get_user_tokens(user_id)
        if token in tokens:
            tokens.remove(token)
            return self.set_user_tokens(user_id, tokens)
        return True

    def get_all_tokens(self) -> Dict[str, Any]:
        """获取所有token（仅用于调试）"""
        if not self.is_connected():
            return {}

        try:
            token_keys = self.redis_client.keys("token:*")
            result = {}
            for key in token_keys:
                value = self.redis_client.get(key)
                if value:
                    result[key] = json.loads(value)
            return result
        except Exception as e:
            print(f"获取所有token失败: {e}")
            return {}

    def flush_all_tokens(self) -> bool:
        """清空所有token（仅用于测试）"""
        if not self.is_connected():
            return False

        try:
            token_keys = self.redis_client.keys("token:*")
            user_token_keys = self.redis_client.keys("user_tokens:*")
            all_keys = token_keys + user_token_keys

            if all_keys:
                self.redis_client.delete(*all_keys)
            return True
        except Exception as e:
            print(f"清空token失败: {e}")
            return False

    def save_to_queues(self, card_number: str, user_id: str, filename: str,
                       formatted_expire: str, status: bool, message: str) -> bool:
        """
        将验证结果保存到两个队列中

        Args:
            card_number: 卡号
            user_id: 用户ID
            filename: 文件名
            formatted_expire: 格式化过期时间
            status: 验证状态
            message: 验证消息

        Returns:
            bool: 保存是否成功
        """
        try:
            # 创建数据记录
            record = {

                "card_number": card_number,
                "user_id": user_id,
                "filename": filename,
                "formatted_expire": formatted_expire,
                "status": status,
                "message": message,
                "timestamp": time.time(),
                "created_at": time.strftime("%Y-%m-%d %H:%M:%S")
            }

            # 序列化数据
            record_json = json.dumps(record, ensure_ascii=False)

            # 保存到两个队列（使用RPUSH添加到队列末尾）
            if status:
                self.redis_client.rpush(self.queue1_key, record_json)
            else:
                self.redis_client.rpush(self.queue2_key, record_json)

            print(f"数据已保存到两个队列: {record['id']}")
            return True

        except Exception as e:
            print(f"保存数据到队列失败: {e}")
            return False

    def publish(self, channel: str, message: str) -> bool:
        """发布消息到Redis频道"""
        if not self.is_connected():
            return False
        if isinstance(message, (dict, list)):
            message = json.dumps(message, ensure_ascii=False)
        try:
            result = self.redis_client.publish(channel, message)
            return result > 0
        except Exception as e:
            print(f"发布消息失败: {e}")
            return False

    def read_from_queue(self, queue_name: str, timeout: int = 0) -> Optional[Dict[str, Any]]:
        """
        从指定队列读取一条数据（阻塞方式）

        Args:
            queue_name: 队列名称 ('queue1' 或 'queue2')
            timeout: 超时时间（秒），0表示无限等待

        Returns:
            Optional[Dict]: 数据记录，超时返回None
        """
        try:
            queue_key = f"verification_queue:{queue_name}"

            # 使用BLPOP阻塞读取（从左端读取）
            result = self.redis_client.blpop(queue_key, timeout=timeout)

            if result:
                _, record_json = result
                record = json.loads(record_json)

                # 将数据标记为处理中（防止客户端崩溃导致数据丢失）
                processing_key = f"{self.processing_key_prefix}{record['id']}"
                self.redis_client.setex(processing_key, 300, record_json)  # 5分钟过期

                print(f"从队列 {queue_name} 读取数据: {record['id']}")
                return record

            return None

        except Exception as e:
            print(f"从队列读取数据失败: {e}")
            return None

    def acknowledge_message(self, record_id: str) -> bool:
        """
        确认消息处理完成，删除处理中的标记

        Args:
            record_id: 记录ID

        Returns:
            bool: 操作是否成功
        """
        try:
            processing_key = f"{self.processing_key_prefix}{record_id}"
            result = self.redis_client.delete(processing_key)

            if result > 0:
                print(f"消息 {record_id} 已确认处理完成")
                return True
            else:
                print(f"消息 {record_id} 未找到或已过期")
                return False

        except Exception as e:
            print(f"确认消息失败: {e}")
            return False

    def get_queue_length(self, queue_name: str) -> int:
        """
        获取队列长度

        Args:
            queue_name: 队列名称

        Returns:
            int: 队列长度
        """
        try:
            queue_key = f"verification_queue:{queue_name}"
            return self.redis_client.llen(queue_key)
        except Exception as e:
            print(f"获取队列长度失败: {e}")
            return 0

    def get_processing_count(self) -> int:
        """
        获取正在处理的消息数量

        Returns:
            int: 处理中的消息数量
        """
        try:
            pattern = f"{self.processing_key_prefix}*"
            keys = self.redis_client.keys(pattern)
            return len(keys)
        except Exception as e:
            print(f"获取处理中消息数量失败: {e}")
            return 0

    def requeue_expired_messages(self) -> int:
        """
        重新入队过期的处理中消息（防止消息丢失）

        Returns:
            int: 重新入队的消息数量
        """
        try:
            pattern = f"{self.processing_key_prefix}*"
            keys = self.redis_client.keys(pattern)
            requeued_count = 0

            for key in keys:
                record_json = self.redis_client.get(key)
                if record_json:
                    record = json.loads(record_json)

                    # 重新加入队列
                    self.redis_client.rpush(self.queue1_key, record_json)
                    self.redis_client.rpush(self.queue2_key, record_json)

                    # 删除处理中标记
                    self.redis_client.delete(key)
                    requeued_count += 1
                    print(f"重新入队过期消息: {record['id']}")

            return requeued_count

        except Exception as e:
            print(f"重新入队过期消息失败: {e}")
            return 0

# 创建全局Redis客户端实例
redis_client = RedisClient()